package com.qg.base.utils;


import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 批量插入 工具
 */
@Slf4j
public class BatchInsertUtil {

    public static <T> void saveDataTest(String tableName, Connection conn, List<List<String>> dataList, List<String> fieldList) throws SQLException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {

        String fieldNames = fieldList.stream().collect(Collectors.joining(","));

        String questionMarks = fieldList.stream().map(field -> "?").collect(Collectors.joining(","));

        String sql = "INSERT INTO %s(%s)values (%s)";
        sql = String.format(sql, tableName, fieldNames, questionMarks);

        conn.setAutoCommit(false);
        PreparedStatement pst = conn.prepareStatement(sql);
        //分批插入数据
        List<List<String>> temp = new ArrayList<>();
        int insertCount = 0;
        for (int i = 1; i <= dataList.size(); i++) {
            temp.add(dataList.get(i - 1));
            if (i % 15000 == 0 || i == dataList.size()) {
                for (List<String> list : temp) {
                    for (int j = 0; j < fieldList.size(); j++) {
                        pst.setString(j + 1, list.get(j));
                    }
                    pst.addBatch();
                }
                pst.executeBatch();
                conn.commit();
                log.info("第{}批插入结束", insertCount++);
                temp = new ArrayList<List<String>>();
            }
        }
        pst.close();
    }


    public static <T> void saveData(String tableName, Connection conn, List<T> infoList) throws SQLException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        saveData(tableName, conn, infoList, false);
    }

    /**
     * @param conn 连接
     */
    public static <T> void saveData(String tableName, Connection conn, List<T> infoList, boolean filterUseLessFields) throws SQLException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        if (infoList == null || infoList.isEmpty()) {
            return;
        }
        Object o = null;
        for (Object value : infoList) {
            if (value != null) {
                o = value;
                break;
            }
        }
        if (o == null) {
            return;
        }


        Field[] declaredFields = o.getClass().getDeclaredFields();
        List<Field> fieldList = Arrays.stream(declaredFields).filter(field -> !Modifier.isStatic(field.getModifiers())).collect(Collectors.toList());

        if (filterUseLessFields) {
            fieldList = removeUseLessFields(fieldList, infoList);
        }

        String fieldNames = fieldList.stream()
                .map(Field::getName)
                .map(BatchInsertUtil::humpToUnderline)
                .collect(Collectors.joining(","));
        String questionMarks = fieldList.stream().map(field -> "?").collect(Collectors.joining(","));

        String sql = "INSERT INTO %s(%s)values (%s)";
        sql = String.format(sql, tableName, fieldNames, questionMarks);

        conn.setAutoCommit(false);
        PreparedStatement pst = conn.prepareStatement(sql);
        List<List<T>> split = BatchUtil.split(infoList, 150000);
        int insertCount = 0;
        for (List<T> ts : split) {
            for (Object obj : ts) {
                if (obj == null) {
                    continue;
                }
                for (int i = 0; i < fieldList.size(); i++) {
                    Object value = ReflectUtil.getGetMethodValue(obj, fieldList.get(i));
                    if (value instanceof Date) {
                        Date date = (Date) value;
                        pst.setTimestamp(i + 1, new Timestamp(date.getTime()));
                    } else {
                        pst.setObject(i + 1, value);
                    }
                }
                pst.addBatch();
            }
            // 需要添加rewriteBatchedStatements=true这个参数，不然即使用executeBatch他还是一条一条插入
            pst.executeBatch();
            conn.commit();
            log.info("第{}批插入结束", insertCount++);
        }
        pst.close();
    }

    /**
     * 删除没有插入数据的字段
     * @param fieldList 字段列表
     * @param objs 插入数据集
     * @return
     * @param <T> 插入数据类型
     */
    private static <T> List<Field> removeUseLessFields(List<Field> fieldList, List<T> objs) {
        List<Field> list = new ArrayList<>();
        for (Field field : fieldList) {
            try {
                boolean hasFields = false;
                for (T obj : objs) {
                    Object value = ReflectUtil.getGetMethodValue(obj, field);
                    if (value != null) {
                        hasFields = true;
                        break;
                    }
                }
                if (hasFields) {
                    list.add(field);
                } else {
                    log.info("{}字段被过滤", field.getName());
                }
            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return list;
    }


    public static String humpToUnderline(String para) {
        StringBuilder sb = new StringBuilder(para);
        // 偏移量，第i个下划线的位置是 当前的位置+ 偏移量（i-1）,第一个下划线偏移量是0
        int temp = 0;
        for (int i = 0; i < para.length(); i++) {
            if (Character.isUpperCase(para.charAt(i))) {
                sb.insert(i + temp, "_");
                temp += 1;
            }
        }
        return sb.toString().toLowerCase();
    }

    public static <T> boolean save2Db(String tableName, List<T> list, Connection conn) throws SQLException {
        try {
            BatchInsertUtil.saveData(tableName, conn, list);
        } catch (NoSuchMethodException e) {
            log.error("未构造get,set方法");
            return true;
        } catch (IllegalAccessException e) {
            log.error("IllegalAccessException:" + e.toString());
            return true;
        } catch (InvocationTargetException e) {
            log.error("InvocationTargetException" + e.toString());
            return true;
        }
        list.clear();
        return false;
    }

    /**
     * 获取oracle的连接信息
     *
     * @param dataSourceConstant oracleConstant
     * @return 连接信息的R对象
     */
   /* public static Connection getConnectionHasError(DataSourceConstant dataSourceConstant) {
        Connection conn;
        try {
            Class.forName(dataSourceConstant.getDriverClassName());
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("找不到驱动错误:" + wrapper(e).toString());
        }
        try {
            conn = DriverManager.getConnection(dataSourceConstant.getUrl(), dataSourceConstant.getUsername(), dataSourceConstant.getPassword());
        } catch (SQLException e) {
            throw new RuntimeException("SQLException:" + wrapper(e).toString());
        }
        if (conn == null) {
            throw new RuntimeException("获取连接失败");
        }
        return conn;
    }*/

    /**
     * 获取oracle的连接信息
     *
     * @param dataSourceConstant oracleConstant
     * @return 连接信息的R对象
     */
    /*public static R<Connection> getConnection(DataSourceConstant dataSourceConstant) {
        Connection conn;
        try {
            Class.forName(dataSourceConstant.getDriverClassName());
        } catch (ClassNotFoundException e) {
            return R.errorAndLog("找不到驱动错误:" + wrapper(e).toString());
        }
        try {
            conn = DriverManager.getConnection(dataSourceConstant.getUrl(), dataSourceConstant.getUsername(), dataSourceConstant.getPassword());
        } catch (SQLException e) {
            return R.errorAndLog("SQLException:" + wrapper(e).toString());
        }
        if (conn == null) {
            return R.errorAndLog("获取连接失败");
        }
        return R.ok(conn);
    }*/

    /**
     * 将数据库的 表名 解析成对应的 po类名
     *
     * @param tableName 表名
     * @return po类名
     */
    public static String parseClassName(String tableName) {
        // 如果表名为空，直接返回空
        if (tableName == null || tableName.isEmpty()) {
            return null;
        }
        // 用一个StringBuilder来拼接结果字符串
        StringBuilder sb = new StringBuilder();

//        // 如果字段名不包含下划线，直接返回原字符串
//        if (!tableName.contains("_")) {
//            sb.append(Character.toUpperCase(tableName.charAt(0)));
//            sb.append(tableName.substring(1).toLowerCase());
//        }

        // 用下划线分割字段名为单词数组
        String[] words = tableName.split("_");
        // 遍历每个单词
        for (String word : words) {
            // 先将首字母转成大写，再将剩余部分转成小写，并追加到结果中
            sb.append(Character.toUpperCase(word.charAt(0)));
            sb.append(word.substring(1).toLowerCase());
        }
        return sb.toString();
    }
}
